package com.immediatus.engine.collision;

import android.util.FloatMath;
import com.immediatus.engine.primitive.Line;
import com.immediatus.engine.shape.RectangularShape;
import com.immediatus.graphics.camera.Camera;

public class RectangularCollisionChecker extends ShapeCollisionChecker{
    private static final float DEG_TO_RAD = (float) (Math.PI) / 180.0f;

    private static final int RECTANGULARSHAPE_VERTEX_COUNT = 4;
    private static final int LINE_VERTEX_COUNT = 2;

    private static final float[] VERTICES_CONTAINS_TMP = new float[2 * RECTANGULARSHAPE_VERTEX_COUNT];
    private static final float[] VERTICES_COLLISION_TMP_A = new float[2 * RECTANGULARSHAPE_VERTEX_COUNT];
    private static final float[] VERTICES_COLLISION_TMP_B = new float[2 * RECTANGULARSHAPE_VERTEX_COUNT];

    public static boolean checkContains(final RectangularShape shape_, final float x_, final float y_){
        RectangularCollisionChecker.fillVertices(shape_, VERTICES_CONTAINS_TMP);
        return ShapeCollisionChecker.checkContains(VERTICES_CONTAINS_TMP, 2 * RECTANGULARSHAPE_VERTEX_COUNT, x_, y_);
    }

    public static boolean isVisible(final Camera camera_, final RectangularShape rShape_){
        RectangularCollisionChecker.fillVertices(camera_, VERTICES_COLLISION_TMP_A);
        RectangularCollisionChecker.fillVertices(rShape_, VERTICES_COLLISION_TMP_B);

        return ShapeCollisionChecker.checkCollision(
                2 * RECTANGULARSHAPE_VERTEX_COUNT, VERTICES_COLLISION_TMP_A,
                2 * RECTANGULARSHAPE_VERTEX_COUNT, VERTICES_COLLISION_TMP_B);
    }

    public static boolean isVisible(final Camera camera_, final Line line_){
        RectangularCollisionChecker.fillVertices(camera_, VERTICES_COLLISION_TMP_A);
        LineCollisionChecker.fillVertices(line_, VERTICES_COLLISION_TMP_B);

        return ShapeCollisionChecker.checkCollision(
                2 * RECTANGULARSHAPE_VERTEX_COUNT, VERTICES_COLLISION_TMP_A,
                2 * LINE_VERTEX_COUNT, VERTICES_COLLISION_TMP_B);
    }

    public static boolean checkCollision(final RectangularShape shapeA_, final RectangularShape shapeB_){
        RectangularCollisionChecker.fillVertices(shapeA_, VERTICES_COLLISION_TMP_A);
        RectangularCollisionChecker.fillVertices(shapeB_, VERTICES_COLLISION_TMP_B);

        return ShapeCollisionChecker.checkCollision(
                2 * RECTANGULARSHAPE_VERTEX_COUNT, VERTICES_COLLISION_TMP_A,
                2 * RECTANGULARSHAPE_VERTEX_COUNT, VERTICES_COLLISION_TMP_B);
    }

    public static boolean checkCollision(final RectangularShape shape_, final Line line_){
        RectangularCollisionChecker.fillVertices(shape_, VERTICES_COLLISION_TMP_A);
        LineCollisionChecker.fillVertices(line_, VERTICES_COLLISION_TMP_B);

        return ShapeCollisionChecker.checkCollision(
                2 * RECTANGULARSHAPE_VERTEX_COUNT, VERTICES_COLLISION_TMP_A,
                2 * LINE_VERTEX_COUNT, VERTICES_COLLISION_TMP_B);
    }

    public static void fillVertices(final RectangularShape shape_, final float[] vertices_){
        final float left = 0;
        final float top = 0;
        final float right = shape_.getWidth();
        final float bottom = shape_.getHeight();

        vertices_[0 + VERTEX_INDEX_X] = left;
        vertices_[0 + VERTEX_INDEX_Y] = top;
        vertices_[2 + VERTEX_INDEX_X] = right;
        vertices_[2 + VERTEX_INDEX_Y] = top;
        vertices_[4 + VERTEX_INDEX_X] = right;
        vertices_[4 + VERTEX_INDEX_Y] = bottom;
        vertices_[6 + VERTEX_INDEX_X] = left;
        vertices_[6 + VERTEX_INDEX_Y] = bottom;

        shape_.getLocalToLayerTransformation().transform(vertices_);
    }

    private static void fillVertices(final Camera camera_, final float[] vertices_){
        vertices_[0 + VERTEX_INDEX_X] = camera_.getMinX();
        vertices_[0 + VERTEX_INDEX_Y] = camera_.getMinY();
        vertices_[2 + VERTEX_INDEX_X] = camera_.getMaxX();
        vertices_[2 + VERTEX_INDEX_Y] = camera_.getMinY();
        vertices_[4 + VERTEX_INDEX_X] = camera_.getMaxX();
        vertices_[4 + VERTEX_INDEX_Y] = camera_.getMaxY();
        vertices_[6 + VERTEX_INDEX_X] = camera_.getMinX();
        vertices_[6 + VERTEX_INDEX_Y] = camera_.getMaxY();

        rotateAroundCenter(vertices_, camera_.getRotation(), camera_.getCenterX(), camera_.getCenterY());
    }

    private static float[] rotateAroundCenter(final float[] vertices_, final float rotation_, final float rotationCenterX_, final float rotationCenterY_){
        if (rotation_ != 0){
            final float rotationRad = DEG_TO_RAD * rotation_;
            final float sinRotationRad = FloatMath.sin(rotationRad);
            final float cosRotationInRad = FloatMath.cos(rotationRad);

            for (int i = vertices_.length - 2; i >= 0; i -= 2){
                final float pX = vertices_[i];
                final float pY = vertices_[i + 1];
                vertices_[i] = rotationCenterX_ +
                        (cosRotationInRad * (pX - rotationCenterX_) - sinRotationRad * (pY - rotationCenterY_));
                vertices_[i + 1] = rotationCenterY_ +
                        (sinRotationRad * (pX - rotationCenterX_) + cosRotationInRad * (pY - rotationCenterY_));
            }
        }
        return vertices_;
    }

}
